import time
from env_init.environment import deliveryManEnv
import Config


# 定义一个Poin类型
class Point:
    """
    表示一个点，判断两个point坐标是否相等
    """
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if self.x == other.x and self.y == other.y:
            return True
        return False

    def __str__(self):
        return "x:" + str(self.x) + 'y:'+str(self.y)


# 定义一个Astar算法来实现最优路径的选择
class AStar:
    """
    算法运行在python3.6环境下执行
    """
    class Node:
        """
        存储在astar中的节点数据
        """
        def __init__(self, point, endpoint, g=0):
            self.point = point   # 自己的坐标
            self.father = None   # 父节点
            self.g = g           # 计算g值，g值在用到的时候需要重新计算
            self.h = (abs(endpoint.x - point.x) + abs(endpoint.y - point.y))*10  # 计算h值

    def __init__(self, startpoint, endpoint, passtag=0,env = None,player = 0):
        """
        构造Astar算法的启动条件
        :param map2d: array2d类型的寻路数组
        :param startpoint: point类型的寻路起点
        :param endpoint: point类型的寻路终点
        :param passtag: int类型的可行走标记(若地图数据!=passTag即为障碍）

        """
        # 开启表
        self.openList = []
        # 关闭表
        self.closeList = []
        # 寻找地图
        self.map2d = env.toAstarBoard(player = player)
        # 起点终点
        self.startPointNum = startpoint
        x = int(startpoint % Config.CHECKERBOARDSIZE)
        y = int(startpoint / Config.CHECKERBOARDSIZE)

        self.startpoint = Point(x, y)

        x = int(endpoint % Config.CHECKERBOARDSIZE)
        y = int(endpoint / Config.CHECKERBOARDSIZE)

        self.endpoint = Point(x, y)
        # 可行走标记
        self.passtag = passtag

    def getminnode(self):
        """
        获得openlist中F值最小的节点
        :return: Node
        """
        currentnode = self.openList[0]
        for node in self.openList:
            if node.g + node.h < currentnode.g + currentnode.h:
                currentnode = node
        return currentnode

    def pointincloselist(self, point):
        """
        :param point: 判断该点是否在封闭信息表中
        :return:
        """
        for node in self.closeList:
            if node.point == point:
                return True
        return False

    def pointinopenlist(self, point):
        for node in self.openList:
            if node.point == point:
                return node
        return None

    def endpointincloselist(self):
        for node in self.openList:
            if node.point == self.endpoint:
                return node

    def searchnear(self, minf, offsetx, offsety):
        """
        搜索节点周围的点
        :param minf:
        :param offsetx:
        :param offsety:
        :return:
        """
        # 越界检测
        if minf.point.x + offsetx < 0 or minf.point.x + offsetx > self.map2d.w - 1 \
                or minf.point.y + offsety < 0 or minf.point.y + offsety > self.map2d.h - 1:
            return
        # 如果是障碍，就忽略
        if self.map2d[minf.point.x + offsetx][minf.point.y + offsety] != self.passtag:
            return
        # 如果在关闭表中，就忽略
        if self.pointincloselist(Point(minf.point.x + offsetx, minf.point.y + offsety)):
            return
        # 设置单位花费
        if offsetx == 0 or offsety == 0:
            step = 12
        else:
            step = 16
        # 如果不再openList中，就把它加入openlist
        currentnode = self.pointinopenlist(Point(minf.point.x + offsetx, minf.point.y + offsety))
        if not currentnode:
            currentnode = AStar.Node(Point(minf.point.x + offsetx, minf.point.y + offsety), self.endpoint,
                                     g=minf.g + step)
            currentnode.father = minf
            self.openList.append(currentnode)
            return
        # 如果在openList中，判断minF到当前点的G是否更小
        if minf.g + step < currentnode.g:  # 如果更小，就重新计算g值，并且改变father
            currentnode.g = minf + step
            currentnode.father = minf

    def start(self):
        """
        开始寻路
        :return: None或Point列表（路径）
        """
        # 1.将起点放入开启列表
        startnode = AStar.Node(self.startpoint, self.endpoint)
        self.openList.append(startnode)

        # 2.主循环逻辑
        while True:
            # 找到F值最小的点
            minf = self.getminnode()
            # 把这个点加入closeList中，并且在openList中删除它
            self.closeList.append(minf)
            self.openList.remove(minf)
            # 判断这个节点的上下左右节点
            self.searchnear(minf, 0, -1)
            self.searchnear(minf, 0, 1)
            self.searchnear(minf, -1, 0)
            self.searchnear(minf, 1, 0)
            # 判断是否终止
            pointx = self.endpointincloselist()
            if pointx:  # 如果终点在关闭表中，就返回结果
                # print("关闭表中")
                cpoint = pointx
                pathlist = []
                while True:
                    if cpoint.father:
                        pathlist.append(cpoint.point)
                        cpoint = cpoint.father
                    else:
                        # print(pathList)
                        # print(list(reversed(pathList)))
                        # print(pathList.reverse())
                        return list(reversed(pathlist))
            if len(self.openList) == 0:
                return None

    def action(self):

        pathList = self.start()

        if pathList is None:
            return None, 5

        # 获取了pathList变量，即可判断当前坐标在pathlist的index位置，然后判断pahtlist中对应的下一个位置的结果

        # 由于计算速度较快，可以考虑每次都计算当前位置距离最终目标的距离

        # 计算结果时间
        action = pathList[0]
        action = action.y * Config.CHECKERBOARDSIZE + action.x
        move = action - self.startPointNum
        if move == 0:
            move = "S"
        elif move == 1:
            move = "R"

        elif move == -1:
            move = "L"

        elif move == 12:
            move = "D"

        elif move == -12:
            move = "U"

        return move, len(pathList)


# if __name__ == '__main__':
#
#     # 显示地图当前样子
#     map2d.showarray2d()
#     # 创建AStar对象,并设置起点为0,0终点为11,11
#     aStar = AStar(map2d, Point(0, 0), Point(11, 11),env =)
#     # 当前计算开始时间
#     starttime = time.time()
#
#     # 开始寻路
#     pathList = aStar.start()
#     # 遍历路径点,在map2d上以'8'显示
#     for point in pathList:
#         map2d[point.x][point.y] = 8
#         print(point)
#         # print(point)
#
#     print("----------------------")
#
#     endtime = time.time()
#
#     # 再次显示地图
#     map2d.showarray2d()
#
#     # 打印计算时长
#     print('The calculating time is in %.2f seconds' % (endtime - starttime))
